#include "core.hpp"
#include "instructions.hpp"
#include "pointer.hpp"

namespace canyon{

#define casecmd(name)             \
    case instructions::name:      \
        this->cmd_##name(cmd);    \
        break;

#define casecmd_i(name, funcname, index)     \
    case instructions::name:                 \
        this->cmd_##funcname(cmd, index);    \
        break;

#define casecmd_j(name)                  \
    case instructions::name:             \
        this->cmd_##name(cmd);           \
        break;

void VM::run_once(command cmd){
    switch(cmd.code){
        casecmd  (exit)
        casecmd_i(mov_ir0, mov_irx, 0)
        casecmd_i(mov_ir1, mov_irx, 1)
        casecmd_i(mov_ir2, mov_irx, 2)
        casecmd_i(mov_ir3, mov_irx, 3)
        casecmd  (mov_i0m)
        casecmd  (mov_r0r0)
        casecmd  (mov_rr)
        casecmd_i(mov_r0m, mov_rxm, 0)
        casecmd_i(mov_r1m, mov_rxm, 1)
        casecmd_i(mov_r2m, mov_rxm, 2)
        casecmd_i(mov_r3m, mov_rxm, 3)
        casecmd_i(mov_r4m, mov_rxm, 4)
        casecmd_i(mov_r5m, mov_rxm, 5)
        casecmd_i(mov_r6m, mov_rxm, 6)
        casecmd_i(mov_r7m, mov_rxm, 7)
        casecmd  (mov_rm)
        casecmd_i(mov_mr0, mov_mrx, 0)
        casecmd_i(mov_mr1, mov_mrx, 1)
        casecmd_i(mov_mr2, mov_mrx, 2)
        casecmd_i(mov_mr3, mov_mrx, 3)
        casecmd_i(mov_mr4, mov_mrx, 4)
        casecmd_i(mov_mr5, mov_mrx, 5)
        casecmd_i(mov_mr6, mov_mrx, 6)
        casecmd_i(mov_mr7, mov_mrx, 7)
        casecmd  (mov_mm)
        casecmd  (swap_rr)
        casecmd  (swap_mm)
        casecmd_i(swap_r_00, swap_r_0x, 0)
        casecmd_i(swap_r_01, swap_r_0x, 1)
        casecmd_i(swap_r_02, swap_r_0x, 2)
        casecmd_i(swap_r_03, swap_r_0x, 3)
        casecmd_i(swap_r_04, swap_r_0x, 4)
        casecmd_i(swap_r_05, swap_r_0x, 5)
        casecmd_i(swap_r_06, swap_r_0x, 6)
        casecmd_i(swap_r_07, swap_r_0x, 7)
        casecmd  (band)
        casecmd  (bxor)
        casecmd  (bnot)
        casecmd  (bleft)
        casecmd  (bright)
        casecmd  (add)
        casecmd  (sub)
        casecmd  (mul)
        casecmd  (div)
        casecmd  (cmp)
        casecmd_j(jmp)
        casecmd_j(jmp_a_if_b)
        casecmd_j(jmp_a_ifn_b)
        casecmd_j(jmp_adr_ife)
        casecmd_j(jmp_adr_ifne)
        casecmd_j(jmp_adr_ifalb)
        casecmd_j(jmp_adr_ifagb)
        casecmd_j(jmp_adr_ifaleb)
        casecmd_j(jmp_adr_ifageb)
        casecmd  (map_dm)
        casecmd  (info_dev_count)
        casecmd  (call_d)
        default:
            break;
    }
}

void VM::run(size_t entrypoint){
    CodePointer code(&this->memory, entrypoint);
    while(true){
        command cmd = code.get_command();
        this->run_once(cmd);

        if(this->get_codeadr() != code.get()){
            code.jump(this->get_codeadr());
        }
        else{
            if(this->flag_skip){
                code.next(this->step);
                this->flag_skip = false;
            }
            else code.next();
            this->set_codeadr(code.get());
        }
    }
}

#undef casecmd
#undef casecmd_i
#undef casecmd_j

}
